package jpack.sz.parser.l;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import jpack.sz.parser.SzResponseParser;
import jpack.sz.parser.SzResponseParserException;

/**
 * Parser for 7-zip l command response
 * 
 * @author Denis Migol
 * 
 */
public class LSzResponseParser extends SzResponseParser<LSzResponseEntity> {

	private static final String HEADER_LINE = "--";

	private static final String SHORT_LISTING_LINE = "------------------- ----- ------------ ------------  ------------------------";
	private static final String LONG_LISTING_LINE = "----------";

	private static boolean skipLinesUntilNotFound(final BufferedReader br, final String string) throws IOException {
		String line = null;
		boolean found = false;
		while (!found && (line = br.readLine()) != null) {
			if (string.equals(line)) {
				found = true;
			}
		}
		return found;
	}

	private static String getValueAfterEqual(final String line) {
		return line.substring(line.indexOf('=') + 1).trim();
	}

	private static void parseHeader(final LSzResponseEntity entity, final BufferedReader br)
			throws SzResponseParserException {
		final boolean found;
		try {
			found = skipLinesUntilNotFound(br, HEADER_LINE);
		} catch (final IOException e) {
			throw new SzResponseParserException(e);
		}
		if (!found) {
			throw new SzResponseParserException("\"" + HEADER_LINE + "\" not found");
		}
		try {
			String line = null;
			while ((line = br.readLine()) != null) {
				if (line.startsWith("Path")) {
					entity.setPath(getValueAfterEqual(line));
				} else if (line.startsWith("Type")) {
					entity.setType(getValueAfterEqual(line));
				} else if (line.startsWith("Solid")) {
					entity.setSolid(getValueAfterEqual(line));
				} else if (line.startsWith("Blocks")) {
				} else if (line.startsWith("Multivolume")) {
					entity.setMultivolume(getValueAfterEqual(line));
				} else if (line.startsWith("Volumes")) {
					entity.setVolumes(Integer.parseInt(getValueAfterEqual(line)));
					break;
				}
			}
		} catch (final IOException e) {
			throw new SzResponseParserException(e);
		}
	}

	private static void parseBlocks(final LSzResponseEntity entity, final BufferedReader br)
			throws SzResponseParserException {
		String line = null;
		boolean found = false;
		try {
			while (!found && (line = br.readLine()) != null) {
				if (line.equals(SHORT_LISTING_LINE)) {
					parseShortBlocks(entity, br);
					found = true;
				} else if (line.equals(LONG_LISTING_LINE)) {
					parseLongBlocks(entity, br);
					found = true;
				}
			}
		} catch (final IOException e) {
			throw new SzResponseParserException(e);
		}
		if (!found) {
			throw new SzResponseParserException("Couldn't to parse Blocks");
		}
	}

	private static String[] splitSpaces(final String s, final int limit) {
		final ArrayList<String> ret = new ArrayList<String>();
		final int retSize = 0;
		final int sLength = s.length();
		int lastSpaceIndex = 0;
		int count = 1;
		for (int index = 0; index < sLength; index++) {
			final char c = s.charAt(index);
			if (c == ' ') {
				if (count < limit) {
					final String string = s.substring(lastSpaceIndex, index).trim();
					if (!"".equals(string)) {
						count++;
						ret.add(string);
					}
					lastSpaceIndex = index;
				}
			}
		}
		if (lastSpaceIndex < (sLength - 1)) {
			final String string = s.substring(lastSpaceIndex, sLength).trim();
			if (!"".equals(string)) {
				ret.add(string);
			}
		}
		return ret.toArray(new String[retSize]);
	}

	private static LSzResponseEntity.Block parseShortBlock(final String line) throws SzResponseParserException {
		try {
			final LSzResponseEntity.Block ret = new LSzResponseEntity.Block();
			final String[] strings = splitSpaces(line, 6);
			ret.setDate(strings[0]);
			ret.setTime(strings[1]);
			ret.setAttr(strings[2]);
			ret.setSize(Integer.parseInt(strings[3]));
			ret.setCompressed(Integer.parseInt(strings[4]));
			ret.setName(strings[5]);
			return ret;
		} catch (final Exception e) {
			throw new SzResponseParserException(e);
		}
	}

	private static void parseShortBlocks(final LSzResponseEntity entity, final BufferedReader br)
			throws SzResponseParserException {
		try {
			final ArrayList<LSzResponseEntity.Block> blocks = new ArrayList<LSzResponseEntity.Block>();
			String line = null;
			while ((line = br.readLine()) != null) {
				if (SHORT_LISTING_LINE.equals(line)) {
					break;
				} else {
					blocks.add(parseShortBlock(line));
				}
			}
			entity.setBlocks(blocks.subList(0, blocks.size()));
		} catch (final IOException e) {
			throw new SzResponseParserException(e);
		}
	}

	private static LSzResponseEntity.LongBlock parseLongBlock(final BufferedReader br) throws SzResponseParserException {
		try {
			LSzResponseEntity.LongBlock ret = null;
			String line = null;
			while ((line = br.readLine()) != null) {
				if (line.startsWith("Path")) {
					ret = new LSzResponseEntity.LongBlock();
					ret.setPath(getValueAfterEqual(line));
				} else if (line.startsWith("Folder")) {
					ret.setFolder(getValueAfterEqual(line));
				} else if (line.startsWith("Size")) {
					ret.setSize(Integer.parseInt(getValueAfterEqual(line)));
				} else if (line.startsWith("Packed Size")) {
					ret.setPackedSize(Integer.parseInt(getValueAfterEqual(line)));
				} else if (line.startsWith("Modified")) {
					ret.setModified(getValueAfterEqual(line));
				} else if (line.startsWith("Created")) {
					ret.setCreated(getValueAfterEqual(line));
				} else if (line.startsWith("Accessed")) {
					ret.setAccessed(getValueAfterEqual(line));
				} else if (line.startsWith("Attributes")) {
					ret.setAttributes(getValueAfterEqual(line));
				} else if (line.startsWith("Encrypted")) {
					ret.setEncrypted(getValueAfterEqual(line));
				} else if (line.startsWith("Solid")) {
					ret.setSolid(getValueAfterEqual(line));
				} else if (line.startsWith("Commented")) {
					ret.setCommented(getValueAfterEqual(line));
				} else if (line.startsWith("Split Before")) {
					ret.setSplitBefore(getValueAfterEqual(line));
				} else if (line.startsWith("Split After")) {
					ret.setSplitAfter(getValueAfterEqual(line));
				} else if (line.startsWith("CRC")) {
					ret.setCrc(getValueAfterEqual(line));
				} else if (line.startsWith("Host OS")) {
					ret.setHostOS(getValueAfterEqual(line));
				} else if (line.startsWith("Method")) {
					ret.setMethod(getValueAfterEqual(line));
				} else if (line.startsWith("Version")) {
					ret.setVersion(getValueAfterEqual(line));
					break;
				}
			}
			return ret;
		} catch (final Exception e) {
			throw new SzResponseParserException();
		}
	}

	private static void parseLongBlocks(final LSzResponseEntity entity, final BufferedReader br)
			throws SzResponseParserException {
		final ArrayList<LSzResponseEntity.LongBlock> longBlocks = new ArrayList<LSzResponseEntity.LongBlock>();
		LSzResponseEntity.LongBlock block = null;
		while ((block = parseLongBlock(br)) != null) {
			longBlocks.add(block);
		}
		entity.setLongBlocks(longBlocks.subList(0, longBlocks.size()));
	}

	@Override
	public LSzResponseEntity parse(final InputStream in) throws SzResponseParserException {
		final LSzResponseEntity ret = new LSzResponseEntity();
		final BufferedReader br = new BufferedReader(new InputStreamReader(in));
		parseHeader(ret, br);
		parseBlocks(ret, br);
		return ret;
	}

}
